#include "ogrebindingPCH.h"
#include "vector3binding.h"
#include "lightbinding.h"

namespace lua_ogre {

const char *LightClassName = "Light";
LuaLight::LuaLight(Ogre::Light *l) : c_obj(l)
{
	if (c_obj != NULL)
	{
		c_obj->setListener(this);
	}
}

LuaLight::~LuaLight()
{
	if (NULL != c_obj)
	{
		c_obj->setListener(NULL);
		Ogre::SceneManager *scn = c_obj->_getManager();
		if (NULL != scn)
		{
			scn->destroyLight(c_obj);
		}
		else
		{
			delete c_obj;
		}
	}
}

const char* LuaLight::GetName() const
{
	if (NULL == c_obj)
	{
		return "Invalid Light";
	}
	return c_obj->getName().c_str();
}

void LuaLight::objectDestroyed(Ogre::MovableObject*)
{
	if (c_obj != NULL)
	{
		c_obj->setListener(NULL);
		c_obj = NULL;
	}
}

int LuaCreateLight(Ogre::Light *light, lua_State *ls)
{
	void *mem = lua_newuserdata(ls, sizeof(LuaLight));
	if (NULL == mem)
	{
		return 0;
	}

	new(mem) LuaLight(light);
	luaL_getmetatable(ls, LightClassName);
	lua_setmetatable(ls, -2);
	return 1;
}


static int GetPosition(Ogre::Light *self, lua_State *ls)
{
	return LuaCreateVector3(self->getPosition(), ls);
}

static int SetPosition(Ogre::Light *self, lua_State *ls)
{
	if (lua_gettop(ls) != 1 || !luaL_checkudata(ls, 1, Vector3ClassName))
	{
		LogLuaError << "invalid value for camera.position!";
		return 0;
	}

	self->setPosition(*(Ogre::Vector3*)lua_touserdata(ls, 1));
	return 0;
}

static int GetDirection(Ogre::Light *self, lua_State *ls)
{
	if (NULL == self->getParentNode())
	{
		LogLuaError << "can't get entity orientation which doesn't have parent node!";
		return 0;
	}
	return LuaCreateVector3(self->getDirection(), ls);
}

static int SetDirection(Ogre::Light *self, lua_State *ls)
{
	if (lua_gettop(ls) != 1 || !luaL_checkudata(ls, 1, Vector3ClassName))
	{
		LogLuaError << "invalid value for camera.orientation!";
		return 0;
	}
	self->setDirection(*(Ogre::Vector3*)lua_touserdata(ls, 1));
	return 0;
}

static int SetAttenuation(Ogre::Light *self, lua_State *ls)
{
	const char *failed = "Light:setattenuation(range, att1 = 0, att2 = 1, att3 = 0) failed! Invalid parameters!";
	int arg_count = lua_gettop(ls);
	if (0 == arg_count || arg_count > 4)
	{
		LogLuaError << failed;
		return 0;
	}
	
	float range, att1(1.0f), att2(0.0f), att3(.0f);
	if (!lua_isnumber(ls, 1))
	{
		LogLuaError << failed;
		return 0;
	}
	else
	{
		range = (float)lua_tonumber(ls, 1);
	}

	if (arg_count > 1)
	{
		if (lua_isnumber(ls, 2))
		{
			att1 = (float)lua_tonumber(ls, 2);
		}
		else
		{
			LogLuaError << failed;
			return 0;
		}
	}

	if (arg_count > 2)
	{
		if (lua_isnumber(ls, 3))
		{
			att2 = (float)lua_tonumber(ls, 3);
		}
		else
		{
			LogLuaError << failed;
			return 0;
		}
	}

	if (arg_count > 3)
	{
		if (lua_isnumber(ls, 4))
		{
			att3 = (float)lua_tonumber(ls, 4);
		}
		else
		{
			LogLuaError << failed;
			return 0;
		}
	}

	self->setAttenuation(range, att1, att2, att3);
	return 0;
}

static int GetDiffuse(Ogre::Light *self, lua_State *ls)
{
	char value[20];
	sprintf(value, "0x%x", self->getDiffuseColour().getAsARGB());
	lua_pushlstring(ls, value, strlen(value));
	return 1;
}

static int SetDiffuse(Ogre::Light *self, lua_State *ls)
{
	if (lua_gettop(ls) != 1 || !lua_isnumber(ls, 1))
	{
		LogLuaError << "invalid value type for light.diffuse";
		return 0;
	}

	Ogre::ColourValue color;
	color.setAsARGB(Ogre::uint32(lua_tonumber(ls, 1)));
	self->setDiffuseColour(color);
	return 0;
}

static LuaMethod<Ogre::Light> g_light_methods_and_props[] = 
{
	{"position", 0, GetPosition, SetPosition},
	{"direction", 0, GetDirection, SetDirection},
	//{"rotate", Rotate, 0, 0},
	{"SetAttenuation", SetAttenuation, 0, 0},
	{"diffuse", 0, GetDiffuse, SetDiffuse},
	{0, 0, 0, 0}
};

static int LightAlloc(lua_State *ls)
{
	LogLuaError << "use Scene:createlight to create light please!";
	return 0;
}

bool BindLight(lua_State *ls)
{
	typedef LuaClassBind<Ogre::Light, LuaLight> LightBind;

	LuaTypeObject<Ogre::Light> type_obj(
		LightClassName,
		g_light_methods_and_props,
		LightAlloc,
		LightBind::Dealloc,
		LightBind::ToString,
		LightBind::Thunk);

	return LightBind::Register(ls, "ov", type_obj);
}

}